#include "stdafx.h"

#include "../include/Polygon.h"
#include "../include/definitions.h"

using namespace std;

namespace ACCAD
{
    static const int eps = 0.000001;

    inline float sgn(float x)
    {
        return abs(x) < eps ? 0 : x > 0 ? 1 : -1;
    }

    void Polygon::render(Renderer &renderer, bool picked)
    {
        renderer.push(center, theta);
        renderer.render(borderColor, innerColor, vertices, picked);
        if (picked) renderer.border(left, right, bottom, top);
        renderer.pop();
    }

    void Polygon::save(std::ostream &out)
    {
        out.write((const char *)&theta, sizeof(float));
        out << center << borderColor << innerColor;
        size_t tmp = vertices.size();
        out.write((const char *)&tmp, sizeof(size_t));
        for (auto &v : vertices)
            out << v;
    }

    void Polygon::load(std::istream &in)
    {
        in.read((char *)&theta, sizeof(float));
        in >> center >> borderColor >> innerColor;
        size_t tmp;
        in.read((char *)&tmp, sizeof(size_t));
        for (size_t i = 0; i < tmp; i++)
        {
            Vec2 v;
            in >> v;
            vertices.push_back(v);
        }
        reGen();
    }

    vector<Vec2> Polygon::getAnchors()
    {
        vector<Vec2> res;
        for (auto &v : vertices)
            res.push_back(center + v.rotate(0, theta));
        return res;
    }

    Vec2 Polygon::getAnchor(int id)
    {
        return center + vertices[id].rotate(0, theta);
    }

    vector<Vec2> Polygon::getBorder()
    {
        vector<Vec2> points;
        for (auto d : delta)
        {
            float x = d[0] == 1 ? right : (d[0] == -1 ? left : 0);
            float y = d[1] == 1 ? top : (d[1] == -1 ? bottom : 0);
            points.push_back(center + Vec2(x, y).rotate(0, theta));
        }

        return points;
    }

    Vec2 Polygon::getBorder(int id)
    {
        float x = delta[id][0] == 1 ? right : (delta[id][0] == -1 ? left : 0);
        float y = delta[id][1] == 1 ? top : (delta[id][1] == -1 ? bottom : 0);
        return center + Vec2(x, y).rotate(0, theta);
    }

    void Polygon::resize(int id, const Vec2 &to)
    {
        Vec2 oppo = getBorder((id + 4) % 8);
        Vec2 res = ((to - oppo) / 2).rotate({ 0, 0 }, -theta);
        Vec2 delta = { 0, 0 }, rate = { 1, 1 };
        if (id != 2 && id != 6)
        {
            delta.x = res.x;
            rate.x = abs(res.x) * 2 / (right - left);
        }
        if (id != 0 && id != 4)
        {
            delta.y = res.y;
            rate.y = abs(res.y) * 2 / (top - bottom);
        }

        center = oppo + delta.rotate({ 0, 0 }, theta);

        for (auto &v : vertices)
        {
            v.x = sgn(v.x) * max(abs(v.x * rate.x), 0.000001f);
            v.y = sgn(v.y) * max(abs(v.y * rate.y), 0.000001f);
        }

        reGen();
    }

    void Polygon::alter(int id, const Vec2 &to)
    {
        vertices[id] = (to - center).rotate(0, -theta);
        Vec2 delta = getPos(vertices);
        for (auto &v : vertices)
            v = v - delta;
        center = center + delta.rotate(0, theta);

        reGen();
    }

    bool Polygon::isInside(const Vec2 & point)
    {
        const auto &vertices = getAnchors();
        if (vertices.size() < 3)
        {
            return DistanceToLine(point, vertices.front(), vertices.back()) <= 5.0f;
        }
        else
        {
            int   i, j = vertices.size() - 1;
            bool  oddNodes = false;
            float x = point.x, y = point.y;

            for (i = 0; i<vertices.size(); i++)
            {
                if ((vertices[i].y< y && vertices[j].y >= y
                    || vertices[j].y<y && vertices[i].y >= y)
                    && (vertices[i].x <= x || vertices[j].x <= x))
                {
                    oddNodes ^= (vertices[i].x + (y - vertices[i].y) / (vertices[j].y - vertices[i].y)*(vertices[j].x - vertices[i].x) < x);
                }
                j = i;
            }
            return oddNodes;
        }      
    }

    FigureType Polygon::getType()
    {
        return POLYGON;
    }

    Polygon * Polygon::Clone()
    {
        return new Polygon(*this);
    }

    void Polygon::reGen()
    {
        top = bottom = vertices[0].y;
        left = right = vertices[0].x;

        for (auto &v : vertices)
        {
            top = max(top, v.y);
            bottom = min(bottom, v.y);
            left = min(left, v.x);
            right = max(right, v.x);
        }
    }

    Vec2 Polygon::getPos(const std::vector<Vec2> &vertices)
    {
        float top, bottom, left, right;
        top = bottom = vertices[0].y;
        left = right = vertices[0].x;

        for (auto &v : vertices)
        {
            top = max(top, v.y);
            bottom = min(bottom, v.y);
            left = min(left, v.x);
            right = max(right, v.x);
        }

        return { (left + right) / 2, (top + bottom) / 2 };
    }

    Polygon::Polygon(const Color &cborder, const Color &cinner, const std::vector<Vec2> &verts) :
        IFigure(getPos(verts), 0, cborder, cinner),
        vertices(verts)
    {
        for (auto &v : vertices)
            v = v - center;

        reGen();
    }

    Polygon::Polygon()
    {
        
    }
}